<?php
###########################################################################
#  Copyright (C) 2006-2007 Glyphix, Inc. <briz@glyphix.com>               #
#                                                                         #
#  Permission is hereby granted, free of charge, to any person obtaining  #
#  a copy of this software and associated documentation files (the        #
#  "Software"), to deal in the Software without restriction, including    #
#  without limitation the rights to use, copy, modify, merge, publish,    #
#  distribute, sublicense, and/or sell copies of the Software, and to     #
#  permit persons to whom the Software is furnished to do so, subject to  #
#  the following conditions:                                              #
#                                                                         #
#  The above copyright notice and this permission notice shall be         #
#  included in all copies or substantial portions of the Software.        #
#                                                                         #
#  Except as contained in this notice, the name(s) of the above           #
#  copyright holders shall not be used in advertising or otherwise to     #
#  promote the sale, use or other dealings in this Software without       #
#  prior written authorization.                                           #
#                                                                         #
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        #
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     #
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. #
#  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR      #
#  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  #
#  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  #
#  OTHER DEALINGS IN THE SOFTWARE.                                        #
###########################################################################

/**
 * GXParams
 *
 * Collects params from the environment and handles them according to their definition in System.xml.
 * @author		Brad Brizendine <briz@glyphix.com>
 * @link		http://www.glyphix.com/
 * @license		http://opensource.org/licenses/bsd-license.php BSD License
 * @version		1.0
 * @package		GXPage
 * @subpackage	Engine
 * @copyright	Copyright 2006 - 2007, Glyphix, Inc.
 * @uses		GXPage_SubClass
 */

class GXPage_Params extends GXPage_SubClass {

	// holds master list of params
	public $Params;
	// handle
	private $GXCookie;

	/**
	 * getInstance
     * Returns the single unique instance of this class.
	 * @return object unique instance of this object
     */
	public static function getInstance(){
		return SingletonPattern::getSingleton( __CLASS__ );
	}

	/**
	 * __construct
	 *
     * Pulls GXPage params from System.xml.
	 * Ensures GXPage params are first in the list.
	 * @param object GXDOM <Params> node (optional)
	 * @return array collected system params
     */
	public function __construct( $params = null ){
		parent::__construct();
		$this->GXCookie = GXCookie::getInstance();

		// load params
		$this->load($params);
		// return self
		return $this;
	}

	/**
	 * load
	 *
	 * Runs through each node in the provided params (or uses the one in System.xml).
	 * Determines whether to include an Action param.
	 * Assigns values to each param.
	 * @param array of GXDOM objects, one per param <Call><.../></Call>, <Include><.../></Include>
	 * @return boolean
	 */
	public function load( $params = null ){
		// use the provided param config; fall back to System.xml
		$list = $params;
		if( !$list ){
			// get the entire list in the order it appears in System.xml
			// ignore Page and Action, since we're now adding it below
			if( !($list = GXPage::get('SystemConfig')->xpath('//Params/*[ not(name() = \'Page\' or name() = \'Action\') ]')) ){
				trigger_error( 'Unable to get system params, or none defined', E_USER_WARNING );
				$list = array();
			}
		}

		// automatically detect System.xml/Site/Params/*
		$varstr = null;
		switch(true){
			// gxpage-params comes from mod_rewrite
			case isset($_REQUEST['gxpage-params']):
				$varstr = '/' .$_REQUEST['gxpage-params'];
				break;
			// if not mod_rewrite, assume we have a "clean url" style uri after index.php (index.php/Page/Action)
			case isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] != '':
				$varstr = $_SERVER['PATH_INFO'];
				break;
		}
		$vars = $varstr ? explode('/',$varstr) : null;
		$includeaction = false;
		// remove the first element, which is always empty and determine if the second param should be named "action"
		if( is_array($vars) ){
			array_shift($vars);
			// check for the existence of the named action
			if( isset($vars[1]) && GXPage::get('SiteConfig')->getAction($vars[0], $vars[1]) ){
				$includeaction = true;
			}
		}

		// convert gxdom to array and prepend Page and Action to the master list of parameters we're looking for
		$plist['Page'] = GXPage::get('SiteConfig')->getDefaultPage();
		if( $includeaction ){
			$plist['Action'] = null;
		}else{
			unset($this->Params['Action']);
		}
		foreach( $list as $node ){
			$plist[ $node->nodeName ] = $node->nodeValue;
		}

		// load values into the Params array
		$val = null;
		foreach( $plist as $name => $default ){
			if( is_array($vars) ){
				$val = array_shift($vars);
			}
			$this->Params[$name] = (strlen($val) > 0) ? $val : $default;
		}

		// load GET vars
		foreach($_GET as $key=>$val){
			$this->Params[$key] = $val;
		}

		// initialize param values using collect()
		$this->collect();
		return true;
	}

	/**
	 * getConfig
	 * Override's parent method in order to update this::Config values with this::Params.
	 * @param string $format the XML engine (SimpleXML or DOM)
	 * @return GXDOM or the specified engine, or false on failure
	 */
	public function getConfig( $format = null ){
		$this->setConfig($this->Params);
		// set tainted to true so that if we're processing onSuccess or onFailure, different Page/Action values will show up in the new config
		$this->tainted = true;
		return parent::getConfig($format);
	}

	/**
	 * collect
     * Adds site-specific params to collected System params
	 * and finds a value for each from the environment.
	 * @return array
     */
	public function collect(){
		foreach( $this->Params as $Key => $Val ){
			// the value for $Key is derived from __get
			$this->Params[$Key] = $this->$Key;
		}
		trigger_error( 'Found params: ' .print_r($this->Params,true), E_USER_WARNING );
		return $this->Params;
	}

	/**
	 * set
	 * Sets the value for the specified param
	 * @param string $key the name of the param
	 * @param string $val the value of the param
	 * @return boolean
	 */
	public function __set( $key = null, $val = null ){
		if( strlen($key) > 0 ){
			$this->Params[ $key ] = $val;
			$this->tainted = true;
		}
		return true;
	}

	public function __get( $name = null ){
		return $this->get($name);
	}

	/**
	 * get
     * Retrieves the value of the requested param
	 * @name String name of the key
	 * @return Mixed value of the key
     */
	public function get( $name = null ){
		if( !$name ){
			trigger_error( 'Missing Name in get()', E_USER_ERROR );
			return false;
		}

		// check for a special method to determine the value of the specified param
		$method = 'get_' .$name;
		if( method_exists($this,$method) ){
			$thisVal = $this->$method( $this->Params[$name] );
		}else{
			switch(true){
				// check the full request for a parameter of the correct name
				case isset($_REQUEST[$name]) && ((is_array($_REQUEST[$name]) && count($_REQUEST[$name])) || (is_scalar($_REQUEST[$name]) && strlen($_REQUEST[$name]) > 0)):
					$thisVal = $_REQUEST[$name];
					// If it's a cookie, perform a get to decode it
					if( isset($_COOKIE[$name]) && strlen($_COOKIE[$name]) > 0 ){
						$thisVal = $this->GXCookie->get($name);
					}
					break;
				// else, use the value as set in SystemConfig
				default:
					$thisVal = isset($this->Params[$name]) ? $this->Params[$name] : null;
			}
		}

		return $thisVal;
	}

	/**
	 * get_Lang
     * Returns the appropriate language value.
	 * @returns String language
     */
	function get_Lang(){
		$preLang = (isset($this->Params['Lang'])) ? $this->Params['Lang'] : null;
		switch(true){
			// First check for the language set in _GET or _POST
			// don't check _REQUEST, since cookies are in the _REQUEST
			case isset($_GET['Lang']) || isset($_POST['Lang']):
				$where = '_GET[Lang] or _POST[Lang]';
				$lang = ($_GET['Lang']) ? $_GET['Lang'] : $_POST['Lang'];
				break;
			// Next check the latest value from this object
			case isset($this->Params['Lang']):
				$where = 'Params';
				$lang = $this->Params['Lang'];
				break;
			// Next check the Lang cookie
			case isset($_COOKIE['Lang']):
				$where = '_COOKIE[Lang]';
				$lang = $this->GXCookie->get('Lang');
				break;
			// Pull it from the language header
			case isset($_SERVER['HTTP_ACCEPT_LANGUAGE']):
				$where = '_SERVER[HTTP_ACCEPT_LANGUAGE] (' .$_SERVER['HTTP_ACCEPT_LANGUAGE'] .')';
				$langStr = explode(',',$_SERVER['HTTP_ACCEPT_LANGUAGE']);
				// capitalize country code
				$tmp = explode('-',$langStr[0]);
				$lang = $tmp[0] .'-' .strtoupper($tmp[1]);
				break;
			// if we still don't have a language, set it to the first <lang> node in SystemConfig
			default:
				$where = 'SystemConfig';
				// get the Lang param node
				$langnode = GXPage::get('SystemConfig')->xpath('//i18n/Languages/*[1]',0);
				// use it if we got one, else default to the static one
				$lang = $langnode ? $langnode->nodeValue : GXPage::$Lang;
		}
		trigger_error( 'Value for Language found in ' .$where .' (' .$preLang .')', E_USER_WARNING );
		// if preLang is different from lang, set the cookie
		if( $preLang != $lang ){
			GXPage::get('GXCookie')->set( 'Lang', $lang );
			trigger_error( 'Setting Language from "' .$preLang .'" to "' .$lang .'"', E_USER_WARNING );
		}

		return $lang;
	}

	/**
	 * getLanguage
     * Wrapper for get_Lang.
	 * @returns String language
     */
	function getLanguage(){
		return $this->get_Lang();
	}

	/**
	 * loadCallerParams
	 * Finds params specified by the caller and loads each one.
	 * The caller might be <Action><Call><.../></Call></Action> or <Include><Item...><.../></Item></Include> or <Page><Params><../></Params></Page>
	 * @param object $item
	 * @return array or false on failure.
	 */
	public function loadCallerParams( $item = null ){
		if( !is_object($item) ){
			trigger_error( 'Params should be provided as a GXDOM object', E_USER_ERROR );
			return false;
		}
		$list = null;
		return $this->load($list);
	}

	/**
	 * loadItemParams
	 * Loads the params found in a <Call> or other block, adding them to the master Params var.
	 * @param string $page (required)
	 * @param string $action (optional)
	 * @return array or false on failure
	 */
	public function loadItemParams( $page = null, $action = null ){
		$item = null;
		if( !($item = GXPage::get('SiteConfig')->getItem($page,$action)) ){
			trigger_error( 'Unable to load item config for ' .$page .'/' .$action, E_USER_ERROR );
			return false;
		}

		// get the params we need to load
		switch( $item->nodeName ){
			// action params are stored within Call
			case 'Action':
				$list = $item->exec->xpath('Call/*');
				break;
			// pages don't have params at this point
			case 'Page':
				$list = $item->exec->xpath('Params/*');
				break;
			// this is probably an <Include> block
			default:
				$list = $item->exec->xpath('*');
		}

		// load it all over again
		return $this->load($list);
	}

}

?>